package cn.mst.vrs.service.order;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.mst.vrs.common.entity.PageParam;
import cn.mst.vrs.controller.admin.order.vo.OrderPageReqVO;
import cn.mst.vrs.controller.admin.order.vo.OrderRespVO;
import cn.mst.vrs.controller.admin.order.vo.OrderSaveReqVO;
import cn.mst.vrs.controller.app.order.vo.ReservationOrderReqVO;
import cn.mst.vrs.controller.app.order.vo.ReservationOrderRespVO;
import cn.mst.vrs.convert.OrderConvert;
import cn.mst.vrs.dal.dataobject.order.OrderDO;
import cn.mst.vrs.dal.dataobject.ordertype.OrderTypeDO;
import cn.mst.vrs.dal.dataobject.pay.PayDO;
import cn.mst.vrs.dal.dataobject.user.UserDO;
import cn.mst.vrs.dal.dataobject.venue.VenueDO;
import cn.mst.vrs.dal.mysql.order.OrderMapper;
import cn.mst.vrs.dal.mysql.ordertype.OrderTypeMapper;
import cn.mst.vrs.dal.mysql.pay.PayMapper;
import cn.mst.vrs.dal.mysql.user.VrsUserMapper;
import cn.mst.vrs.dal.mysql.venue.VenueMapper;
import cn.mst.vrs.enums.OrderTypeEnum;
import cn.mst.vrs.enums.PayStatusEnum;
import cn.mst.vrs.enums.ReservationOrderStatusEnum;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.mst.vrs.enums.ErrorCodeConstants.*;

/**
 * 订单 Service 实现类
 *
 * @author 超级管理员
 */
@Service
@Validated
public class OrderServiceImpl implements OrderService {

    @Resource
    private OrderMapper orderMapper;

    @Resource
    private OrderTypeMapper orderTypeMapper;

    @Resource
    private PayMapper payMapper;

    @Resource
    private VenueMapper venueMapper;

    @Resource
    private VrsUserMapper vrsUserMapper;

    @Override
    public Long createOrder(OrderSaveReqVO createReqVO) {
        // 插入
        OrderDO order = BeanUtils.toBean(createReqVO, OrderDO.class);
        orderMapper.insert(order);
        // 返回
        return order.getId();
    }

    @Override
    public void updateOrder(OrderSaveReqVO updateReqVO) {
        // 校验存在
        validateOrderExists(updateReqVO.getId());
        // 更新
        OrderDO updateObj = BeanUtils.toBean(updateReqVO, OrderDO.class);
        orderMapper.updateById(updateObj);
    }

    @Override
    public void deleteOrder(Long id) {
        // 校验存在
        validateOrderExists(id);
        // 删除
        orderMapper.deleteById(id);
    }

    private void validateOrderExists(Long id) {
        if (orderMapper.selectById(id) == null) {
            throw exception(ORDER_NOT_EXISTS);
        }
    }

    @Override
    public OrderRespVO getOrder(Long id) {
        OrderDO orderDO = orderMapper.selectById(id);
        if (ObjUtil.isNull(orderDO)) {
            throw exception(ORDER_NOT_EXISTS);
        }
        OrderTypeDO orderTypeDO = orderTypeMapper.selectById(orderDO.getTypeId());
        if (ObjUtil.isNull(orderTypeDO)) {
            throw exception(ORDER_TYPE_NOT_EXISTS);
        }
        VenueDO venueDO = venueMapper.selectById(orderDO.getItemId());
        if (ObjUtil.isNull(venueDO)) {
            throw exception(VENUE_NOT_EXISTS);
        }
        UserDO userDO = vrsUserMapper.selectById(orderDO.getUserId());
        if (ObjUtil.isNull(userDO)) {
            throw exception(USER_NOT_EXISTS);
        }
        return OrderConvert.INSTANCE.toRespVO(orderDO, orderTypeDO.getCode(), venueDO.getName(), userDO.getNickName());
    }

    @Override
    public PageResult<OrderRespVO> getOrderPage(OrderPageReqVO pageReqVO) {
        PageResult<OrderDO> pageResult = orderMapper.selectPage(pageReqVO);
        List<OrderDO> tempList = pageResult.getList();
        Set<Long> typeIdSet = tempList.stream().map(OrderDO::getTypeId).collect(Collectors.toSet());
        Map<Long, OrderTypeDO> typeMap = new HashMap<>();
        orderTypeMapper.selectBatchIds(typeIdSet).forEach((orderType) -> typeMap.put(orderType.getId(), orderType));
        Set<Long> itemIdSet = tempList.stream().map(OrderDO::getItemId).collect(Collectors.toSet());
        Map<Long, VenueDO> venueMap = new HashMap<>();
        venueMapper.selectBatchIds(itemIdSet).forEach((venue) -> venueMap.put(venue.getId(), venue));
        Set<Long> userIdSet = tempList.stream().map(OrderDO::getUserId).collect(Collectors.toSet());
        Map<Long, UserDO> userMap = new HashMap<>();
        vrsUserMapper.selectBatchIds(userIdSet).forEach((vrsUser) -> userMap.put(vrsUser.getId(), vrsUser));
        List<OrderRespVO> list = tempList.stream().map(order -> OrderConvert.INSTANCE.toRespVO(order, typeMap.get(order.getTypeId()).getCode(), userMap.get(order.getUserId()).getNickName(), venueMap.get(order.getItemId()).getName())).toList();
        return new PageResult<>(list, pageResult.getTotal());
    }

    @Transactional
    @Override
    public String reservation(ReservationOrderReqVO reqVO, Long userId) {
        Long venueId = reqVO.getVenueId();

        VenueDO venueDO = validVenueExists(venueId);

        OrderTypeDO orderType = orderTypeMapper.selectOne(OrderTypeDO::getCode, OrderTypeEnum.RESERVATION.getCode());
        Map<String, Object> extendInfoMap = new HashMap<>() {{
            put("startedAt", reqVO.getStartedAt());
            put("endedAt", reqVO.getEndedAt());
        }};
        String extendInfo = JSON.toJSONString(extendInfoMap);
        OrderDO order = OrderDO.builder()
                .typeId(orderType.getId())
                .userId(userId)
                .itemId(venueId)
                .count(reqVO.getCount())
                .total(reqVO.getTotal())
                .extendInfo(extendInfo)
                .status(ReservationOrderStatusEnum.WAIT_PAID.getStatus())
                .build();
        orderMapper.insert(order);
        PayDO pay = PayDO.builder()
                .orderId(order.getId())
                .dueAmount(reqVO.getTotal())
                .status(PayStatusEnum.WAIT_NOTIFIED.getStatus())
                .build();
        payMapper.insert(pay);
        return String.valueOf(pay.getId());
    }

    @Override
    public List<ReservationOrderRespVO> currentReservation(Long userId) {
        List<OrderDO> orderList = orderMapper.currentOrder(userId);
        if (CollUtil.isEmpty(orderList)) {
            return new ArrayList<>();
        }
        List<Long> venueIdList = orderList.stream().map(OrderDO::getItemId).toList();
        Map<Long, VenueDO> venueMap = new HashMap<>();
        venueMapper.selectBatchIds(venueIdList).forEach((venue) -> {
            venueMap.put(venue.getId(), venue);
        });
        return orderList.stream().map(order -> {
            String extendInfo = order.getExtendInfo();
            Map<String, String> extendInfoMap = JSON.parseObject(extendInfo, HashMap.class);
            LocalDateTime startedAt = LocalDateTimeUtil.parse(extendInfoMap.get("startedAt"));
            LocalDateTime endedAt = LocalDateTimeUtil.parse(extendInfoMap.get("endedAt"));
            return OrderConvert.INSTANCE.toReservationOrderRespVO(order, venueMap.get(order.getItemId()), startedAt, endedAt);
        }).toList();
    }

    @Override
    public cn.mst.vrs.common.entity.PageResult<ReservationOrderRespVO> historyReservation(Long userId, PageParam pageParam) {
        Page<OrderDO> orderDOPage = orderMapper.historyOrder(userId, Page.of(pageParam.getCurrent(), pageParam.getSize()));
        List<OrderDO> orderList = orderDOPage.getRecords();
        if (CollUtil.isEmpty(orderList)) {
            return cn.mst.vrs.common.entity.PageResult.of(orderDOPage.getCurrent(), orderDOPage.getTotal(), new ArrayList<>());
        }
        List<Long> venueIdList = orderList.stream().map(OrderDO::getItemId).toList();
        Map<Long, VenueDO> priceMap = new HashMap<>();
        venueMapper.selectBatchIds(venueIdList).forEach((venue) -> {
            priceMap.put(venue.getId(), venue);
        });
        String pattern = "HH:mm";
        List<ReservationOrderRespVO> list = orderList.stream().map(order -> {
            String extendInfo = order.getExtendInfo();
            Map<String, String> extendInfoMap = JSON.parseObject(extendInfo, HashMap.class);
            LocalDateTime startedAt = LocalDateTimeUtil.parse(extendInfoMap.get("startedAt"), pattern);
            LocalDateTime endedAt = LocalDateTimeUtil.parse(extendInfoMap.get("endedAt"), pattern);
            return OrderConvert.INSTANCE.toReservationOrderRespVO(order, priceMap.get(order.getItemId()), startedAt, endedAt);
        }).toList();
        return cn.mst.vrs.common.entity.PageResult.of(orderDOPage.getCurrent(), orderDOPage.getTotal(), list);
    }

    private VenueDO validVenueExists(Long venueId) {
        VenueDO venue = venueMapper.selectById(venueId);
        if (ObjUtil.isNull(venue)) {
            throw exception(VENUE_NOT_EXISTS);
        }
        return venue;
    }

}